home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/kernel/drivers/char/tty_ioctl.c
- *
- * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
- *
- * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
- * which can be dynamically activated and de-activated by the line
- * discipline handling modules (like SLIP).
- */
-
- #include <linux/types.h>
- #include <linux/termios.h>
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/config.h>
- #include <linux/kernel.h>
- #include <linux/major.h>
- #include <linux/tty.h>
- #include <linux/fcntl.h>
- #include <linux/string.h>
-
- #include <asm/io.h>
- #include <asm/bitops.h>
- #include <asm/segment.h>
- #include <asm/system.h>
-
- #undef DEBUG
- #ifdef DEBUG
- # define PRINTK(x) printk (x)
- #else
- # define PRINTK(x) /**/
- #endif
-
- #undef __notyet__
-
- extern int session_of_pgrp(int pgrp);
- extern int do_screendump(int arg);
- extern int kill_pg(int pgrp, int sig, int priv);
-
- #ifdef CONFIG_SELECTION
- extern int set_selection(const int arg);
- extern int paste_selection(struct tty_struct *tty);
- #endif /* CONFIG_SELECTION */
-
- static int tty_set_ldisc(struct tty_struct *tty, int ldisc);
-
- void flush_input(struct tty_struct * tty)
- {
- cli();
- tty->read_q.head = tty->read_q.tail = 0;
- tty->secondary.head = tty->secondary.tail = 0;
- tty->canon_head = tty->canon_data = tty->erasing = 0;
- memset(&tty->readq_flags, 0, sizeof tty->readq_flags);
- memset(&tty->secondary_flags, 0, sizeof tty->secondary_flags);
- sti();
- if (!tty->link)
- return;
- /* No cli() since ptys don't use interrupts. */
- tty->link->write_q.head = tty->link->write_q.tail = 0;
- wake_up_interruptible(&tty->link->write_q.proc_list);
- if (tty->link->packet) {
- tty->ctrl_status |= TIOCPKT_FLUSHREAD;
- wake_up_interruptible(&tty->link->secondary.proc_list);
- }
- }
-
- void flush_output(struct tty_struct * tty)
- {
- cli();
- tty->write_q.head = tty->write_q.tail = 0;
- sti();
- wake_up_interruptible(&tty->write_q.proc_list);
- if (!tty->link)
- return;
- /* No cli() since ptys don't use interrupts. */
- tty->link->read_q.head = tty->link->read_q.tail = 0;
- tty->link->secondary.head = tty->link->secondary.tail = 0;
- tty->link->canon_head = tty->link->canon_data = tty->link->erasing = 0;
- memset(&tty->link->readq_flags, 0, sizeof tty->readq_flags);
- memset(&tty->link->secondary_flags, 0, sizeof tty->secondary_flags);
- if (tty->link->packet) {
- tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
- wake_up_interruptible(&tty->link->secondary.proc_list);
- }
- }
-
- void wait_until_sent(struct tty_struct * tty, int timeout)
- {
- struct wait_queue wait = { current, NULL };
-
- TTY_WRITE_FLUSH(tty);
- if (EMPTY(&tty->write_q))
- return;
- add_wait_queue(&tty->write_q.proc_list, &wait);
- current->counter = 0; /* make us low-priority */
- if (timeout)
- current->timeout = timeout + jiffies;
- else
- current->timeout = (unsigned) -1;
- do {
- current->state = TASK_INTERRUPTIBLE;
- if (current->signal & ~current->blocked)
- break;
- TTY_WRITE_FLUSH(tty);
- if (EMPTY(&tty->write_q))
- break;
- schedule();
- } while (current->timeout);
- current->state = TASK_RUNNING;
- remove_wait_queue(&tty->write_q.proc_list, &wait);
- }
-
- static int do_get_ps_info(int arg)
- {
- struct tstruct {
- int flag;
- int present[NR_TASKS];
- struct task_struct tasks[NR_TASKS];
- };
- struct tstruct *ts = (struct tstruct *)arg;
- struct task_struct **p;
- char *c, *d;
- int i, n = 0;
-
- i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
- if (i)
- return i;
- for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
- if (*p)
- {
- c = (char *)(*p);
- d = (char *)(ts->tasks+n);
- for (i=0 ; i<sizeof(struct task_struct) ; i++)
- put_fs_byte(*c++, d++);
- put_fs_long(1, (unsigned long *)(ts->present+n));
- }
- else
- put_fs_long(0, (unsigned long *)(ts->present+n));
- return(0);
- }
-
- static void unset_locked_termios(struct termios *termios,
- struct termios *old,
- struct termios *locked)
- {
- int i;
-
- #define NOSET_MASK(x,y,z) (x = ((x) & ~(z)) | ((y) & (z)))
-
- if (!locked) {
- printk("Warning?!? termios_locked is NULL.\n");
- return;
- }
-
- NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
- NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
- NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
- NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
- termios->c_line = locked->c_line ? old->c_line : termios->c_line;
- for (i=0; i < NCCS; i++)
- termios->c_cc[i] = locked->c_cc[i] ?
- old->c_cc[i] : termios->c_cc[i];
- }
-
- int check_change(struct tty_struct * tty, int channel)
- {
- /* If we try to set the state of terminal and we're not in the
- foreground, send a SIGTTOU. If the signal is blocked or
- ignored, go ahead and perform the operation. POSIX 7.2) */
- if (current->tty != channel)
- return 0;
- if (tty->pgrp <= 0) {
- printk("check_change: tty->pgrp <= 0!\n");
- return 0;
- }
- if (current->pgrp == tty->pgrp)
- return 0;
- if (is_ignored(SIGTTOU))
- return 0;
- if (is_orphaned_pgrp(current->pgrp))
- return -EIO;
- (void) kill_pg(current->pgrp,SIGTTOU,1);
- return -ERESTARTSYS;
- }
-
- static int set_termios_2(struct tty_struct * tty, struct termios * termios)
- {
- struct termios old_termios = *tty->termios;
- int canon_change;
-
- canon_change = (old_termios.c_lflag ^ termios->c_lflag) & ICANON;
- cli();
- *tty->termios = *termios;
- if (canon_change) {
- memset(&tty->secondary_flags, 0, sizeof tty->secondary_flags);
- tty->canon_head = tty->secondary.tail;
- tty->canon_data = 0;
- tty->erasing = 0;
- }
- sti();
- if (canon_change && !L_ICANON(tty) && !EMPTY(&tty->secondary))
- /* Get characters left over from canonical mode. */
- wake_up_interruptible(&tty->secondary.proc_list);
-
- /* see if packet mode change of state */
-
- if (tty->link && tty->link->packet) {
- int old_flow = ((old_termios.c_iflag & IXON) &&
- (old_termios.c_cc[VSTOP] == '\023') &&
- (old_termios.c_cc[VSTART] == '\021'));
- int new_flow = (I_IXON(tty) &&
- STOP_CHAR(tty) == '\023' &&
- START_CHAR(tty) == '\021');
- if (old_flow != new_flow) {
- tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
- if (new_flow)
- tty->ctrl_status |= TIOCPKT_DOSTOP;
- else
- tty->ctrl_status |= TIOCPKT_NOSTOP;
- wake_up_interruptible(&tty->link->secondary.proc_list);
- }
- }
-
- unset_locked_termios(tty->termios, &old_termios,
- termios_locked[tty->line]);
-
- if (tty->set_termios)
- (*tty->set_termios)(tty, &old_termios);
-
- return 0;
- }
-
- static int set_termios(struct tty_struct * tty, struct termios * termios,
- int channel)
- {
- struct termios tmp_termios;
-
- memcpy_fromfs(&tmp_termios, termios, sizeof (struct termios));
- return set_termios_2(tty, &tmp_termios);
- }
-
- static int get_termio(struct tty_struct * tty, struct termio * termio)
- {
- int i;
- struct termio tmp_termio;
-
- i = verify_area(VERIFY_WRITE, termio, sizeof (struct termio));
- if (i)
- return i;
- tmp_termio.c_iflag = tty->termios->c_iflag;
- tmp_termio.c_oflag = tty->termios->c_oflag;
- tmp_termio.c_cflag = tty->termios->c_cflag;
- tmp_termio.c_lflag = tty->termios->c_lflag;
- tmp_termio.c_line = tty->termios->c_line;
- for(i=0 ; i < NCC ; i++)
- tmp_termio.c_cc[i] = tty->termios->c_cc[i];
- memcpy_tofs(termio, &tmp_termio, sizeof (struct termio));
- return 0;
- }
-
- static int set_termio(struct tty_struct * tty, struct termio * termio,
- int channel)
- {
- struct termio tmp_termio;
- struct termios tmp_termios;
-
- tmp_termios = *tty->termios;
- memcpy_fromfs(&tmp_termio, termio, sizeof (struct termio));
-
- #define SET_LOW_BITS(x,y) ((x) = (0xffff0000 & (x)) | (y))
-
- SET_LOW_BITS(tmp_termios.c_iflag, tmp_termio.c_iflag);
- SET_LOW_BITS(tmp_termios.c_oflag, tmp_termio.c_oflag);
- SET_LOW_BITS(tmp_termios.c_cflag, tmp_termio.c_cflag);
- SET_LOW_BITS(tmp_termios.c_lflag, tmp_termio.c_lflag);
- memcpy(&tmp_termios.c_cc, &tmp_termio.c_cc, NCC);
-
- #undef SET_LOW_BITS
-
- return set_termios_2(tty, &tmp_termios);
- }
-
- static int set_window_size(struct tty_struct * tty, struct winsize * ws)
- {
- struct winsize tmp_ws;
-
- memcpy_fromfs(&tmp_ws, ws, sizeof (struct winsize));
- if (memcmp(&tmp_ws, &tty->winsize, sizeof (struct winsize)) &&
- tty->pgrp > 0)
- kill_pg(tty->pgrp, SIGWINCH, 1);
- tty->winsize = tmp_ws;
- return 0;
- }
-
- /* Set the discipline of a tty line. */
- static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
- {
- if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS) ||
- !(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
- return -EINVAL;
-
- if (tty->disc == ldisc)
- return 0; /* We are already in the desired discipline */
-
- /* Shutdown the current discipline. */
- wait_until_sent(tty, 0);
- flush_input(tty);
- if (ldiscs[tty->disc].close)
- ldiscs[tty->disc].close(tty);
-
- /* Now set up the new line discipline. */
- tty->disc = ldisc;
- tty->termios->c_line = ldisc;
- if (ldiscs[tty->disc].open)
- return(ldiscs[tty->disc].open(tty));
- else
- return 0;
- }
-
- static unsigned long inq_canon(struct tty_struct * tty)
- {
- int nr, head, tail;
-
- if (!tty->canon_data)
- return 0;
- head = tty->canon_head;
- tail = tty->secondary.tail;
- nr = (head - tail) & (TTY_BUF_SIZE-1);
- /* Skip EOF-chars.. */
- while (head != tail) {
- if (test_bit(tail, &tty->secondary_flags) &&
- tty->secondary.buf[tail] == __DISABLED_CHAR)
- nr--;
- INC(tail);
- }
- return nr;
- }
-
- int tty_ioctl(struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg)
- {
- struct tty_struct * tty;
- struct tty_struct * other_tty;
- struct tty_struct * termios_tty;
- pid_t pgrp;
- int dev;
- int termios_dev;
- int retval;
-
- if (MAJOR(file->f_rdev) != TTY_MAJOR) {
- printk("tty_ioctl: tty pseudo-major != TTY_MAJOR\n");
- return -EINVAL;
- }
- dev = MINOR(file->f_rdev);
- tty = TTY_TABLE(dev);
- if (!tty)
- return -EINVAL;
- if (IS_A_PTY(dev))
- other_tty = tty_table[PTY_OTHER(dev)];
- else
- other_tty = NULL;
- if (IS_A_PTY_MASTER(dev)) {
- termios_tty = other_tty;
- termios_dev = PTY_OTHER(dev);
- } else {
- termios_tty = tty;
- termios_dev = dev;
- }
- switch (cmd) {
- case TCGETS:
- retval = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof (struct termios));
- if (retval)
- return retval;
- memcpy_tofs((struct termios *) arg,
- termios_tty->termios,
- sizeof (struct termios));
- return 0;
- case TCSETSF:
- case TCSETSW:
- case TCSETS:
- retval = check_change(termios_tty, termios_dev);
- if (retval)
- return retval;
- if (cmd == TCSETSF || cmd == TCSETSW) {
- if (cmd == TCSETSF)
- flush_input(termios_tty);
- wait_until_sent(termios_tty, 0);
- }
- return set_termios(termios_tty, (struct termios *) arg,
- termios_dev);
- case TCGETA:
- return get_termio(termios_tty,(struct termio *) arg);
- case TCSETAF:
- case TCSETAW:
- case TCSETA:
- retval = check_change(termios_tty, termios_dev);
- if (retval)
- return retval;
- if (cmd == TCSETAF || cmd == TCSETAW) {
- if (cmd == TCSETAF)
- flush_input(termios_tty);
- wait_until_sent(termios_tty, 0);
- }
- return set_termio(termios_tty, (struct termio *) arg,
- termios_dev);
- case TCXONC:
- retval = check_change(tty, dev);
- if (retval)
- return retval;
- switch (arg) {
- case TCOOFF:
- stop_tty(tty);
- break;
- case TCOON:
- start_tty(tty);
- break;
- case TCIOFF:
- if (STOP_CHAR(tty) != __DISABLED_CHAR)
- put_tty_queue(STOP_CHAR(tty),
- &tty->write_q);
- break;
- case TCION:
- if (START_CHAR(tty) != __DISABLED_CHAR)
- put_tty_queue(START_CHAR(tty),
- &tty->write_q);
- break;
- default:
- return -EINVAL;
- }
- return 0;
- case TCFLSH:
- retval = check_change(tty, dev);
- if (retval)
- return retval;
- switch (arg) {
- case TCIFLUSH:
- flush_input(tty);
- break;
- case TCIOFLUSH:
- flush_input(tty);
- /* fall through */
- case TCOFLUSH:
- flush_output(tty);
- break;
- default:
- return -EINVAL;
- }
- return 0;
- case TIOCEXCL:
- set_bit(TTY_EXCLUSIVE, &tty->flags);
- return 0;
- case TIOCNXCL:
- clear_bit(TTY_EXCLUSIVE, &tty->flags);
- return 0;
- case TIOCSCTTY:
- if (current->leader &&
- (current->session == tty->session))
- return 0;
- /*
- * The process must be a session leader and
- * not have a controlling tty already.
- */
- if (!current->leader || (current->tty >= 0))
- return -EPERM;
- if (tty->session > 0) {
- /*
- * This tty is already the controlling
- * tty for another session group!
- */
- if ((arg == 1) && suser()) {
- /*
- * Steal it away
- */
- struct task_struct *p;
-
- for_each_task(p)
- if (p->tty == dev)
- p->tty = -1;
- } else
- return -EPERM;
- }
- current->tty = dev;
- tty->session = current->session;
- tty->pgrp = current->pgrp;
- return 0;
- case TIOCGPGRP:
- retval = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof (pid_t));
- if (retval)
- return retval;
- if (current->tty != termios_dev)
- return -ENOTTY;
- put_fs_long(termios_tty->pgrp, (pid_t *) arg);
- return 0;
- case TIOCSPGRP:
- retval = check_change(termios_tty, termios_dev);
- if (retval)
- return retval;
- if ((current->tty < 0) ||
- (current->tty != termios_dev) ||
- (termios_tty->session != current->session))
- return -ENOTTY;
- pgrp = get_fs_long((pid_t *) arg);
- if (pgrp < 0)
- return -EINVAL;
- if (session_of_pgrp(pgrp) != current->session)
- return -EPERM;
- termios_tty->pgrp = pgrp;
- return 0;
- case TIOCOUTQ:
- retval = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof (unsigned long));
- if (retval)
- return retval;
- put_fs_long(CHARS(&tty->write_q),
- (unsigned long *) arg);
- return 0;
- case TIOCINQ:
- retval = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof (unsigned long));
- if (retval)
- return retval;
- if (L_ICANON(tty))
- put_fs_long(inq_canon(tty),
- (unsigned long *) arg);
- else
- put_fs_long(CHARS(&tty->secondary),
- (unsigned long *) arg);
- return 0;
- case TIOCSTI:
- if ((current->tty != dev) && !suser())
- return -EPERM;
- retval = verify_area(VERIFY_READ, (void *) arg, 1);
- if (retval)
- return retval;
- put_tty_queue(get_fs_byte((char *) arg), &tty->read_q);
- TTY_READ_FLUSH(tty);
- return 0;
- case TIOCGWINSZ:
- retval = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof (struct winsize));
- if (retval)
- return retval;
- memcpy_tofs((struct winsize *) arg, &tty->winsize,
- sizeof (struct winsize));
- return 0;
- case TIOCSWINSZ:
- if (IS_A_PTY_MASTER(dev))
- set_window_size(other_tty,(struct winsize *) arg);
- return set_window_size(tty,(struct winsize *) arg);
- case TIOCLINUX:
- switch (get_fs_byte((char *)arg))
- {
- #ifdef __notyet__
- case 0:
- return do_screendump(arg);
- #endif
- case 1:
- return do_get_ps_info(arg);
- #ifdef CONFIG_SELECTION
- case 2:
- return set_selection(arg);
- case 3:
- return paste_selection(tty);
- case 4:
- unblank_screen();
- return 0;
- #endif /* CONFIG_SELECTION */
- default:
- return -EINVAL;
- }
- case TIOCCONS:
- if (IS_A_CONSOLE(dev)) {
- if (!suser())
- return -EPERM;
- redirect = NULL;
- return 0;
- }
- if (redirect)
- return -EBUSY;
- if (!suser())
- return -EPERM;
- if (IS_A_PTY_MASTER(dev))
- redirect = other_tty;
- else if (IS_A_PTY_SLAVE(dev))
- redirect = tty;
- else
- return -ENOTTY;
- return 0;
- case FIONBIO:
- arg = get_fs_long((unsigned long *) arg);
- if (arg)
- file->f_flags |= O_NONBLOCK;
- else
- file->f_flags &= ~O_NONBLOCK;
- return 0;
- case TIOCNOTTY:
- if (current->tty != dev)
- return -ENOTTY;
- if (current->leader)
- disassociate_ctty(0);
- current->tty = -1;
- return 0;
- case TIOCGETD:
- retval = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof (unsigned long));
- if (retval)
- return retval;
- put_fs_long(tty->disc, (unsigned long *) arg);
- return 0;
- case TIOCSETD:
- retval = check_change(tty, dev);
- if (retval)
- return retval;
- arg = get_fs_long((unsigned long *) arg);
- return tty_set_ldisc(tty, arg);
- case TIOCGLCKTRMIOS:
- arg = get_fs_long((unsigned long *) arg);
- retval = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof (struct termios));
- if (retval)
- return retval;
- memcpy_tofs((struct termios *) arg,
- &termios_locked[termios_dev],
- sizeof (struct termios));
- return 0;
- case TIOCSLCKTRMIOS:
- if (!suser())
- return -EPERM;
- arg = get_fs_long((unsigned long *) arg);
- memcpy_fromfs(&termios_locked[termios_dev],
- (struct termios *) arg,
- sizeof (struct termios));
- return 0;
- case TIOCPKT:
- if (!IS_A_PTY_MASTER(dev))
- return -ENOTTY;
- retval = verify_area(VERIFY_READ, (void *) arg,
- sizeof (unsigned long));
- if (retval)
- return retval;
- if (get_fs_long(arg)) {
- if (!tty->packet) {
- tty->packet = 1;
- tty->link->ctrl_status = 0;
- }
- } else
- tty->packet = 0;
- return 0;
- case TCSBRK: case TCSBRKP:
- retval = check_change(tty, dev);
- if (retval)
- return retval;
- wait_until_sent(tty, 0);
- if (!tty->ioctl)
- return 0;
- tty->ioctl(tty, file, cmd, arg);
- return 0;
- default:
- if (tty->ioctl) {
- retval = (tty->ioctl)(tty, file, cmd, arg);
- if (retval != -EINVAL)
- return retval;
- }
- if (ldiscs[tty->disc].ioctl) {
- retval = (ldiscs[tty->disc].ioctl)
- (tty, file, cmd, arg);
- return retval;
- }
- return -EINVAL;
- }
- }
-